#ifndef KERNEL_MEMORY_PHYSICALHEAP_H
#define KERNEL_MEMORY_PHYSICALHEAP_H

#include <Mint/Types.h>
#include "Memory/MemoryManager.h"
#include "Utils/Spinlock.h"

#define PHYSICAL_HEAP_ZONE_SIGNATURE			( 0x2EA7BEEF )
#define PHYSICAL_HEAP_MAX_ZONE_COUNT            ( 8 )
#define PHYSICAL_HEAP_ZONE_IN_USE				( 1 << 0 )
#define PHYSICAL_HEAP_CHUNK_SIGNATURE			( 0xBE57BEEF )
#define PHYSICAL_HEAP_WALL_SIGNATURE			( 0xDEADBEEF )

class CSpinlock;
typedef struct PhysicalHeapNode_s PhysicalHeapNode_t;
typedef struct PhysicalHeapList_s PhysicalHeapList_t;
typedef struct PhysicalHeapZone_s PhysicalHeapZone_t;
typedef struct PhysicalHeapChunk_s PhysicalHeapChunk_t;

struct PhysicalHeapNode_s
{
	PhysicalHeapNode_t * fNext;
}
_PACKED;

struct PhysicalHeapList_s
{
	PhysicalHeapNode_t * fFirst;
	uint32 fCount;
}
_PACKED;

struct PhysicalHeapZone_s
{
	uint32 fSignature;
	Address fBaseAddress;
	Size fChunkSize;
	uint32 fChunkCount;
	PhysicalHeapList_t fFreeChunks;
	uint32 fFlags;
#if MEMORY_USE_WALLS
	uint32 fWall;
#endif
}
_PACKED;

struct PhysicalHeapChunk_s
{
	uint32 fSignature;
	Size fChunkSize;
#if MEMORY_USE_WALLS
	uint32 fWall;
#endif
}
_PACKED;

class CPhysicalHeap
{
public:
	Result Initialize( void );
	void * Allocate( Size size );
	void Free( void * memory );

    Size GetSize( void ) { return fSize; }
	Size GetUsed( void ) { return fOffset; }
    Size GetAvailable( void ) { return fSize - fOffset; }
	Size GetAllocated( void ) { return fAllocated; }
	Size GetFreed( void ) { return fFreed; }

private:
	void * _AllocateFromZone( int index, Size size );
	void * _AllocateChunk( Size size );
	void * _AllocatePages( Size size );
	void _FreeToZone( void * memory );
	void _FreeChunk( void * memory );
	void _FreePage( void * memory );

private:
	CSpinlock fSpinlock;
	Address fBaseAddress;
	Size fOffset;
	Size fSize;
	PhysicalHeapList_t fFreePages;
	PhysicalHeapZone_t * fZones[ PHYSICAL_HEAP_MAX_ZONE_COUNT ];
	Size fZoneSizes[ PHYSICAL_HEAP_MAX_ZONE_COUNT ];
	Size fUsed;
	Size fAllocated;
	Size fFreed;
};

extern CPhysicalHeap * PhysicalHeap;

#endif // KERNEL_MEMORY_PHYSICALHEAP_H
